//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================

#include <ddk.h>
#include <malloc.h>
#include <string.h>
#include <stdio.h>
#include <serialio.h>
#include "arch/cc.h"

static IDeviceDriver *pSerial;

#define SERVER_TEL_NO "841" // Tel. No. of PPP Server
#define TRY_CONNECT_TIMES 3

// Self-check AT commands
static char *ATCheckCmds[] = {
                    "AT",
                    "ATE0V1",
                    "AT",
                    "ATS0=0",
                    "AT",
                    "ATE0V1",
                    "AT",
                    NULL
                };


inline CARAPI NetSleep(int ms, WaitResult *pResult)
{
    if (INFINITE == ms) {
        DzSleep();
        if (NULL != pResult) *pResult = WaitResult_Interrupted;
    }
    else {
        DzSleep(DzMillisecondsToTicks(ms), pResult);
    }

    return NOERROR;
}

//extern int PppRead(char* buf);
//extern int PppWrite(char* buf, int len);

typedef void * sio_fd_t;
extern "C" s32_t sio_read(sio_fd_t, char* buf, u32_t length)
{
	// blocking mode
	int len = 0;

//	len = PppRead(buf);
    return len;
}

extern "C" s32_t sio_write(sio_fd_t, char * buf, u32_t len)
{
    #if 0
    WaitResult wr;
    int ret = 0;
    int senlen = 0;
    while(len > 0) {
    	ret = PppWrite(buf + senlen, len);
    	if(ret == -1) {
    		return ret;
    	}
    	len -= ret;
    	senlen += ret;
    	if(len > 0) {
    		NetSleep(100, &wr);
    	}
    }
//    #if 0
    kprintf("\n=====write data:%d=====\n", len);
    for(unsigned int i = 0; i < len; i++) {
    	if(buf[i] == 0x7d) {
    		buf[i + 1] -= 0x20;
    	}
    	else {
    		kprintf("%02x ", buf[i] & 0xff);
    	}
    }
    kprintf("\nwrite============================end\n");
    #endif
    return len;
}

extern "C" void sio_read_abort(sio_fd_t)
{
}

extern "C" u32_t sys_jiffies(void) /* since power up. */
{
	return DzGetElapsedTicks();
}

extern "C" void LwipSleep(int ms)
{
	DzSleep(ms/10, NULL);
}

/*initialize the serial device*/
Boolean StartATC()
{
    ECode ec;
    SERIAL_INFO new_info;
    UInt32 blocking;


    // set serial info
    new_info.baud = NUM_SERIAL_BAUD_115200;
    new_info.stop = NUM_SERIAL_STOP_1;
    new_info.parity = NUM_SERIAL_PARITY_NONE;
    new_info.word_length = NUM_SERIAL_WORD_LENGTH_8;
    new_info.flags =
        NUM_SERIAL_FLOW_DSRDTR_RX |
        NUM_SERIAL_FLOW_DSRDTR_TX |
        NUM_SERIAL_FLOW_RTSCTS_RX |
        NUM_SERIAL_FLOW_DSRDTR_TX;
    MemoryBuf ezData((Byte *)&new_info, sizeof(new_info));
    ec = pSerial->Control(
        SERIALCONTROL_SETCONFIG | IO_SET_CONFIG_SERIAL_INFO,
        ezData,
        NULL,
        NULL);
    // blocking mode
    blocking = FALSE;
    MemoryBuf ezBlocking((Byte *)&blocking, sizeof(blocking));
    ec = pSerial->Control(
        SERIALCONTROL_SETCONFIG | IO_SET_CONFIG_READ_BLOCKING,
        ezBlocking,
        NULL,
        NULL);
    if(FAILED(ec)) {
        kprintf("set com port error:%x\n", ec);
        return FALSE;
    }
	ec = pSerial->Control(
        SERIALCONTROL_SETCONFIG | IO_SET_CONFIG_WRITE_BLOCKING,
        ezBlocking,
        NULL,
        NULL);
    if(FAILED(ec)) {
        kprintf("set com port error:%x\n", ec);
        return FALSE;
    }
    return TRUE;
}

Boolean Transmit(const void *bits, Int32 length)
{
    ECode ec;
    MemoryBuf_<128> ebbInData;
    ebbInData.Copy((Byte*)bits, length);
    ec = pSerial->Write(0, ebbInData, &length, NULL);
    if (!SUCCEEDED(ec)) {
        kprintf("send data error:%x\n", ec);
        return FALSE;
    }
    return TRUE;
}

Boolean RecvAtResponse(char *pszRcv, unsigned int len, unsigned int *psize)
{
    Boolean rc = FALSE;
    ECode ec;
    MemoryBuf ebbOutData((Byte *)pszRcv, 100);
    NetSleep(500, NULL);
    ec = pSerial->Read(0, 100, &ebbOutData, NULL);
    if (!SUCCEEDED(ec)) {
        kprintf("Fail to GetResponse!");
        return FALSE;
    }
//    *psize = strlen(pszRcv);
	*psize = ebbOutData.GetUsed();
    if (*psize == 0) { // read nothing, timeout
        kprintf("GetResponse TIMEOUT!\n");
        return FALSE;
    }
    else { // read a response line
        char *s;
        pszRcv[*psize] = 0;
#define AT_SEPARATOR_LEN 2
       s = pszRcv + AT_SEPARATOR_LEN;

        if (strstr(s, "OK") || strstr(s, "CONNECT")) {
            rc = TRUE;
        }
        else {
            return FALSE;
        }
        kprintf("GetResponse OK!:%s\n", s);
    }

    return rc;
}

int SendAtRequest(char *cmd, int len)
{
    Byte stmp[100];
    if (cmd == NULL) {
        return 0;
    }

    memcpy(stmp, cmd, len);

    stmp[len++] = (Byte)0x0D;
    stmp[len] = '\0';

    if (Transmit(stmp, len)) {
        kprintf("Req: %s\n", stmp);
    }
    else {
        kprintf("send ERROR---->Req: %s\n", stmp);
        return 0;
    }

    return len ;
}

Boolean CheckModemStatus(void)
{
    unsigned int     size;
    char    **ppszCmd = &ATCheckCmds[0];
    char    pszRcv[100] = {0};
    Boolean    bFlag = TRUE;

    while (*ppszCmd) {
        SendAtRequest(*ppszCmd, strlen(*ppszCmd));
        if (RecvAtResponse(pszRcv, sizeof(pszRcv), &size) == FALSE) {
            kprintf("read failed!\n");
            return FALSE;
        }
#if 0
        strtok(pszRcv, "\x0D\x0A");
#endif
        ppszCmd++;
    }

    if(!bFlag) {
        kprintf("AT command: %s error\n", pszRcv);
    }
    kprintf("\nFinish Modem Check!\n");

    return (bFlag);
}

extern "C" int Modem()
{
    unsigned long   times = 0;
    Boolean            bDialSuc = FALSE;
    UInt32 blocking;
    char cRevCont[24];

    kprintf("\n-------start modem connection-------\n");
    if (!StartATC()) {
		return FALSE;
    }

    for(; times < TRY_CONNECT_TIMES; times++) {
        if (CheckModemStatus()) {
            char  szATCommand[50] = "ATDT";
            strcat(szATCommand, SERVER_TEL_NO);
            SendAtRequest(szATCommand, strlen(szATCommand));
            kprintf("\nDailing ...%s\n", SERVER_TEL_NO);

            // blocking mode
            blocking = TRUE;
            MemoryBuf ezBlocking((Byte *)&blocking, sizeof(blocking));
            ECode ec = pSerial->Control(
                SERIALCONTROL_SETCONFIG | IO_SET_CONFIG_READ_BLOCKING,
                ezBlocking,
                NULL,
                NULL);
            if(FAILED(ec)) {
                kprintf("set com port error:%x\n", ec);
                return FALSE;
            }
            MemoryBuf ebbOutData((Byte *)cRevCont, 24);
            ec = pSerial->Read(0, 24, &ebbOutData, NULL);
            if(FAILED(ec)) {
                kprintf("recv connect failed");
				return FALSE;
            }

            if (cRevCont) {
                if (strstr(cRevCont + 2, "CONNECT")) {
                    bDialSuc = TRUE;
                    kprintf("recv data:%s\n", cRevCont + 2);
                    break;
                }
                else {
                    kprintf("recv connect error!\n");
                    break;
                }
            }
        }
        else {
            break;
            kprintf("Dial failure!\n");
            kprintf("Retry....after 30 secondes\n");
            NetSleep(1000 * 10000 * 30, NULL);
        }
    }


    if (bDialSuc) {
		NetSleep(1000, NULL);
        kprintf("\nDial Success, will build a connect to server ...\n");
//		#if defined(_win32)
//		char lcpsrc[4] = {0};
//		#else
//		char lcpsrc[] = {
//			(char)0x7e, (char)0xff, (char)0x7d, (char)0x23,
//			(char)0xc0, (char)0x21, (char)0x7d, (char)0x21,
//	                (char)0x7d, (char)0x21, (char)0x7d, (char)0x20,
//	                (char)0x7d, (char)0x2a, (char)0x7d, (char)0x25,
//		        (char)0x7d, (char)0x26, (char)0x62, (char)0x1c,
//		        (char)0x80, (char)0x39, (char)0xa3, (char)0xc7,
//		        (char)0x7e};
//
//		#endif
	//	UInt32 length;
	//	MemoryBuf ebbOutData(lcpsrc, sizeof(lcpsrc));
	//	pSerial->Write(0, ebbOutData, &length, NULL);
    }
    else {
        kprintf("Dial failure!\n");
		return FALSE;
    }
   return TRUE;

}

extern "C" int GPRS();
int GPRS()
{
	MemoryBuf_<128> ebbInData;
    MemoryBuf_<1024> ebbOutData;
	Int32 length;

	if(!StartATC()) {
	    return -1;
	}
    ebbInData.Copy((Byte *)"at+wmux=0\x0D", 11);
    ECode ec = pSerial->Write(0, ebbInData, &length, NULL);
    if(FAILED(ec)) {
		return -1;
    }
    NetSleep(2000, NULL);
    ec = pSerial->Read(0, 1024, &ebbOutData, NULL);
    if(FAILED(ec)) {
		return -1;
    }
    ebbInData.Copy((Byte *)"AT+CGDCONT=1,\"IP\",\"CMNET\"\x0D", 27);
    kprintf("AT+CGDCONT=1,\"IP\",\"CMNET\"\n");
    ec = pSerial->Write(0, ebbInData, &length, NULL);
    if(FAILED(ec)) {
		return -1;
    }
    NetSleep(2000, NULL);
    ec = pSerial->Read(0, 1024, &ebbOutData, NULL);
    if(FAILED(ec)) {
		return -1;
    }
    ebbInData.Copy((Byte *)"AT+CGQREQ=1,3,4,3,0,0\x0D", 23);
    kprintf("AT+CGQREQ=1,3,4,3,0,0\n");
    ec = pSerial->Write(0, ebbInData, &length, NULL);
    if(FAILED(ec)) {
		return -1;
    }
    NetSleep(2000, NULL);
    ec = pSerial->Read(0, 1024, &ebbOutData, NULL);
    if(FAILED(ec)) {
		return -1;
    }

    ebbInData.Copy((Byte *)"AT+CGATT=1\x0D", 12);
    kprintf("AT+CGATT=1\n");
    ec = pSerial->Write(0, ebbInData, &length, NULL);
    if(FAILED(ec)) {
		return -1;
    }
    NetSleep(2000, NULL);
    ec = pSerial->Read(0, 1024, &ebbOutData, NULL);
    if(FAILED(ec)) {
		return -1;
    }

    ebbInData.Copy((Byte *)"ATD*99***1#\x0D", 13);
    kprintf("ATD*99***1#\n");
    ec = pSerial->Write(0, ebbInData, &length, NULL);
    if(FAILED(ec)) {
		return -1;
    }
    NetSleep(2000, NULL);

    kprintf("\n");
	return 0;
}

